#include "mercenary.h"
#include "ui_mercenary.h"
#include "SpellEffect.h"
#include "Ability.h"
#include "Rotation.h"
#include "RatingConvert.h"
#include <fstream>

Mercenary::Mercenary(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::Mercenary)
{
    ui->setupUi(this);
    string filename = "Mercenary_stats.txt";
    ifstream fin (filename.c_str());
    if(fin.good()){
    Load_Stats();}
    updateStatBoxes();
}

Mercenary::~Mercenary()
{
    delete ui;
}

enum STATS {
    AIM = 0, CUNNING, STRENGTH, WILLPOWER = 3,
    CRIT, SURGE, ALACRITY , POWER , ACCURACY = 8,
    DEFENSE, SHIELD, ABSORB, ARMOR = 12,
    LEVEL, MH_ACC, OH_ACC, ENEMY_LEVEL, ENEMY_ARMOR= 17,
    SPEED,  MH_MAX, MH_MIN, OH_MAX , OH_MIN =22,
    TECH_CRIT_CHANCE , TECH_CRIT_SIZE, TECH_BONUS_HEALING, TECH_BONUS_DAMAGE = 26,
    RANGED_CRIT_CHANCE , RANGED_CRIT_SIZE, RANGED_BONUS_DAMAGE = 29,
    FORCE_CRIT_CHANCE , FORCE_CRIT_SIZE, FORCE_BONUS_HEALING, FORCE_BONUS_DAMAGE = 33,
    MELEE_CRIT_CHANCE , MELEE_CRIT_SIZE, MELEE_BONUS_DAMAGE = 36,
    TECH_POWER, FORCE_POWER = 38
};

//define global Mercenary variables
QString mercRotationOutput;
QString &mercRotationRef = mercRotationOutput;



int HSc_count = 0;
int RSc_count = 0;
int ES_count = 0;
int RSh_count = 0;
int VH_count = 0;
int SCG_count = 0;
int HSc_SCG_count = 0;
int RSc_SCG_count = 0;
int RSh_SCG_count = 0;

//create a holder for the stats
std::vector <double> mercStats(39,0);



void Mercenary::setStats()
{
    int accBuff = 0;
    int critBuff = 3*(ui->skillBox_3->value());
    critBuff += 2*(ui->skillBox_2->value());
    if(ui->smugglerCheckBox->isChecked()) critBuff += 5;
    int techSurgeBuff = 3*(ui->skillBox_4->value());
    //int rangedSurgeBuff = 0;
    //int aimUpgBuff = 3*(ui->skillBox_1->value());
    int alacBuff = 2*(ui->skillBox_6->value());
    if(ui->skillBox_5->value()==2) alacBuff += 5;

    int aim = ui->primaryBox->value();
    if(ui->consularCheckBox->isChecked()) aim *= 1.05;

    mercStats.at(AIM) = aim;
    mercStats.at(CUNNING) = ui->secondaryBox->value();
    mercStats.at(CRIT)= ui->critBox->value();
    mercStats.at(SURGE)= ui->surgeBox->value();
    mercStats.at(POWER)= ui->powerBox->value();
    mercStats.at(TECH_POWER) = ui->techBox->value();
    mercStats.at(ALACRITY)= ui->alacBox->value();
    mercStats.at(LEVEL)= ui->levelBox->value();

    mercStats.at(ACCURACY) = ui->accBox->value();
    mercStats.at(MH_MAX) = ui->MHmaxBox->value();
    mercStats.at(MH_MIN) = ui->MHminBox->value();
    mercStats.at(ENEMY_LEVEL) = ui->enemyLevelBox->value();
    mercStats.at(ENEMY_ARMOR) = ui->enemyArmorBox->value();

    mercStats.at(TECH_CRIT_CHANCE) = ( TechCritChance(mercStats, critBuff, true));
    mercStats.at(TECH_CRIT_SIZE) = ( TechCritSize(mercStats, techSurgeBuff));
    mercStats.at(SPEED) =( Speed(mercStats, alacBuff));
    mercStats.at(TECH_BONUS_HEALING) = (TechBonusHealing(mercStats.at(POWER), mercStats.at(AIM)));
    mercStats.at(MH_ACC) = (MH_Accuracy(mercStats, accBuff));
    mercStats.at(TECH_BONUS_DAMAGE) = TechBonusDamage(mercStats, true);
    mercStats.at(RANGED_CRIT_CHANCE) = ( RangedCritChance(mercStats, critBuff, false));
    mercStats.at(RANGED_CRIT_SIZE) = ( RangedCritSize(mercStats, 0));
    mercStats.at(RANGED_BONUS_DAMAGE) = RangedBonusDamage(mercStats, false);

}

vector <double> Mercenary::calculateStats(vector <double> &stats2){
    int accBuff = 0;
    int critBuff = 3*(ui->skillBox_3->value());
    critBuff += 2*(ui->skillBox_2->value());
    if(ui->smugglerCheckBox->isChecked()) critBuff += 5;
    int techSurgeBuff = 3*(ui->skillBox_4->value());
    int rangedSurgeBuff = 0;
    //int aimUpgBuff = 3*(ui->skillBox_1->value());
    int alacBuff = 2*(ui->skillBox_6->value());
    if(ui->skillBox_5->value()==2) alacBuff += 5;

    stats2.at(TECH_CRIT_CHANCE) = ( TechCritChance(stats2, critBuff, true));
    stats2.at(TECH_CRIT_SIZE) = ( TechCritSize(stats2, techSurgeBuff));
    stats2.at(SPEED) =( Speed(stats2, alacBuff));
    stats2.at(TECH_BONUS_HEALING) = (TechBonusHealing(stats2.at(POWER) ,stats2.at(AIM)));
    stats2.at(MH_ACC) = (MH_Accuracy(stats2, accBuff));
    stats2.at(OH_ACC) = (OH_Accuracy(stats2, accBuff));
    stats2.at(TECH_BONUS_DAMAGE) = TechBonusDamage(stats2);
    stats2.at(RANGED_CRIT_CHANCE) = ( RangedCritChance(stats2, critBuff, false));
    stats2.at(RANGED_CRIT_SIZE) = ( RangedCritSize(stats2, rangedSurgeBuff));
    stats2.at(RANGED_BONUS_DAMAGE) = RangedBonusDamage(stats2, false);
    return stats2;
}

void Mercenary::computeAlacrity(){
    ui->castView->clear();
    ui->countView->clear();
    setStats();
    vector <double> stats2=mercStats;
    QString alacOutput = "Haste%, RSh SCG casts, RSh Casts, SCG usages, HSc+RSc casts, HSc+RSc SCG casts, Total Casts, HPS\n\n";
    double rotationTime = ui->numCasts->value();
    int numCasts = ui->numCasts->value();
    double HPS;
    for(double m=0.0;m<39.5;m+=0.5){
        stats2.at(SPEED)=1-(m/100);
        if(ui->fixedTimeButton->isChecked()){
        HPS = bodyguardRotation(rotationTime,stats2);
        }else{
            HPS = bodyguardRotation(numCasts,stats2);
        }
        alacOutput += QString::number(m) +" , " + QString::number(RSh_SCG_count) +" ,  " + QString::number(RSh_count)
                +" , " + QString::number(SCG_count)+" , " + QString::number(RSc_count+HSc_count)
                + " , " + QString::number(RSc_SCG_count + HSc_SCG_count)+ " , " + QString::number(RSc_count + HSc_count+ES_count + RSh_count)
                +" , " + QString::number(HPS) +"\n";
    }
    ui->countView->insertPlainText(alacOutput);
}

void Mercenary::updateStatBoxes()
{

    setStats();
    ui->techCritChanceLabel->setText(QString::number(mercStats.at(TECH_CRIT_CHANCE)));
    ui->techCritSizeLabel->setText(QString::number(mercStats.at(TECH_CRIT_SIZE)));
    ui->rangedCritChanceLabel->setText(QString::number(mercStats.at(RANGED_CRIT_CHANCE)));
    ui->rangedCritSizeLabel->setText(QString::number(mercStats.at(RANGED_CRIT_SIZE)));
    ui->speedLabel->setText(QString::number(100*(1-mercStats.at(SPEED))));
    ui->bonusHealingLabel->setText(QString::number(mercStats.at(TECH_BONUS_HEALING)));
    ui->MHaccLabel->setText(QString::number(mercStats.at(MH_ACC)));
    ui->OHaccLabel->setText(QString::number(mercStats.at(OH_ACC)));
    ui->techBonusDamageLabel->setText(QString::number(mercStats.at(TECH_BONUS_DAMAGE)));
    ui->rangedBonusDamageLabel->setText(QString::number(mercStats.at(RANGED_BONUS_DAMAGE)));

    ui->accBox->setEnabled(true);
    ui->accBox_2->setEnabled(true);
    ui->accBox_3->setEnabled(true);
    ui->accuracyButton->setEnabled(true);
    ui->accLabel->setEnabled(true);
    ui->accLabel_2->setEnabled(true);
    ui->accLabel_3->setEnabled(true);
    ui->accWeightLabel->setEnabled(true);
    ui->MHaccLabel->setEnabled(true);
    ui->MHaccLabel_2->setEnabled(true);
    ui->MHmaxBox->setEnabled(true);
    ui->MHmaxLabel->setEnabled(true);
    ui->MHminBox->setEnabled(true);
    ui->MHminLabel->setEnabled(true);
    ui->OHmaxBox->setEnabled(true);
    ui->OHmaxLabel->setEnabled(true);
    ui->OHminBox->setEnabled(true);
    ui->OHminLabel->setEnabled(true);
    ui->OHaccLabel->setEnabled(true);
    ui->OHaccLabel_2->setEnabled(true);

    ui->enemyArmorBox->setEnabled(true);
    ui->enemyArmorLabel->setEnabled(true);
    ui->enemyLevelBox->setEnabled(true);
    ui->enemyLevelLabel->setEnabled(true);
    ui->missChanceLabel->setEnabled(true);
    ui->missChanceLabel_2->setEnabled(true);
    ui->accWeightLabel_2->setEnabled(true);
    ui->enemyDRLabel->setEnabled(true);
    ui->enemyDRLabel_2->setEnabled(true);

    if(ui->tree1->isChecked()){
        ui->accBox->setEnabled(false);
        ui->accBox_2->setEnabled(false);
        ui->accBox_3->setEnabled(false);
        ui->accuracyButton->setEnabled(false);
        ui->accLabel->setEnabled(false);
        ui->accLabel_2->setEnabled(false);
        ui->accLabel_3->setEnabled(false);
        ui->accWeightLabel->setEnabled(false);
        ui->MHaccLabel->setEnabled(false);
        ui->MHaccLabel_2->setEnabled(false);
        ui->MHmaxBox->setEnabled(false);
        ui->MHmaxLabel->setEnabled(false);
        ui->MHminBox->setEnabled(false);
        ui->MHminLabel->setEnabled(false);
        ui->OHmaxBox->setEnabled(false);
        ui->OHmaxLabel->setEnabled(false);
        ui->OHminBox->setEnabled(false);
        ui->OHminLabel->setEnabled(false);
        ui->enemyArmorBox->setEnabled(false);
        ui->enemyArmorLabel->setEnabled(false);
        ui->enemyLevelBox->setEnabled(false);
        ui->enemyLevelLabel->setEnabled(false);
        ui->missChanceLabel->setEnabled(false);
        ui->missChanceLabel_2->setEnabled(false);
        ui->accWeightLabel_2->setEnabled(false);
        ui->enemyDRLabel->setEnabled(false);
        ui->enemyDRLabel_2->setEnabled(false);
        ui->OHaccLabel->setEnabled(false);
        ui->OHaccLabel_2->setEnabled(false);
    }
}

double Mercenary::calculateHPS(){
    double HPS = 0;
    setStats();
    double rotationTime = ui->numCasts->value();
    int numCasts = ui->numCasts->value();
    if(ui->fixedTimeButton->isChecked()){
        HPS = bodyguardRotation(rotationTime, mercStats);
    }else{
        HPS = bodyguardRotation(numCasts, mercStats);
    }

    return HPS;
}

double Mercenary::calculateDPS(){
double DPS = 0;
return DPS;
}

void Mercenary::getWeights(){
    //copy stats for modification
    setStats();
    vector <double> stats2 = mercStats;
    double baseHPS, aimHPS, cunningHPS, powerHPS, critHPS, surgeHPS, alacHPS =0, refStat=1;

    double statBuff = 1+0.03*(ui->skillBox_1->value());
    std::vector <double> weights(8,0);
    for(int m=0;m<7;m++){
        stats2 = mercStats;
        switch(m){
        case 0:
            break;
        case 1:
            stats2.at(AIM)+= (1*statBuff);
            break;
        case 2:
            stats2.at(POWER)+=1;
            break;
        case 3:
            stats2.at(CRIT)+=1;
            break;
        case 4:
            stats2.at(SURGE)+=1;
            break;
        case 5:
            stats2.at(ALACRITY)+=1;
            break;
        case 6:
            stats2.at(CUNNING)+=1;
            break;
        default:
            break;
        }
        stats2 = calculateStats(stats2);
        if(ui->fixedTimeButton->isChecked()){
            double numCasts = ui->numCasts->value();
            weights.at(m)=bodyguardRotation(numCasts, stats2);
        }else{
            int numCasts = ui->numCasts->value();
            weights.at(m)=bodyguardRotation(numCasts, stats2);
        }

    }
    baseHPS = weights.at(0);
    aimHPS = weights.at(1) -baseHPS;
    powerHPS = weights.at(2) -baseHPS;
    critHPS = weights.at(3) -baseHPS;
    surgeHPS = weights.at(4) -baseHPS;
    alacHPS = weights.at(5) -baseHPS;
    cunningHPS = weights.at(6) -baseHPS;

    if(ui->powerButton->isChecked())refStat = powerHPS;
    if(ui->aimButton->isChecked())refStat = aimHPS;
    if(ui->critButton->isChecked())refStat = critHPS;
    if(ui->surgeButton->isChecked())refStat = surgeHPS;
    if(ui->alacrityButton->isChecked())refStat = alacHPS;
    //if(ui->accuracyButton->isChecked())refStat = accuracyDPS;

    double powerWeight = powerHPS/refStat;
    double aimWeight = aimHPS/refStat;
    double cunningWeight = cunningHPS/refStat;
    double critWeight = critHPS/refStat;
    double surgeWeight = surgeHPS/refStat;
    double alacWeight = alacHPS/refStat;

    ui->critWeightLabel->setText(QString::number(critWeight));
    ui->surgeWeightLabel->setText(QString::number(surgeWeight));
    ui->alacWeightLabel->setText(QString::number(alacWeight));
    ui->powerWeightLabel->setText(QString::number(powerWeight));
    ui->primaryWeightLabel->setText(QString::number(aimWeight));
    ui->secondaryWeightLabel->setText(QString::number(cunningWeight));
}

void Mercenary::compareUpgrades(){
    setStats();
    vector <double> stats1 = mercStats;
    vector <double> stats2 = mercStats;
    double baseHPS = 0, upg1HPS = 1, upg2HPS = 2;
    double rotationTime = ui->numCasts->value();
    int numCasts = ui->numCasts->value();

    double statBuff = 1 + 0.03*(ui->skillBox_1->value());
    if(ui->consularCheckBox->isChecked()) statBuff += 0.05;

    if(ui->fixedTimeButton->isChecked()){
        baseHPS = bodyguardRotation(rotationTime, mercStats);
    }else{
        baseHPS = bodyguardRotation(numCasts, mercStats);
    }
    //populate upgrade 1
    stats1.at(AIM) += (ui->primaryBox_2->value())*statBuff;
    stats1.at(CUNNING) += ui->secondaryBox_2->value();
    stats1.at(CRIT) += ui->critBox_2->value();
    stats1.at(SURGE) += ui->surgeBox_2->value();
    stats1.at(POWER) += ui->powerBox_2->value() + ui->techBox_2->value();
    stats1.at(ALACRITY) += ui->alacBox_2->value();

    stats1 = calculateStats(stats1);

    if(ui->fixedTimeButton->isChecked()){
        upg1HPS = bodyguardRotation(rotationTime, stats1);
    }else{
        upg1HPS = bodyguardRotation(numCasts, stats1);
    }

    //populate upgrade 2
    stats2.at(AIM) += (ui->primaryBox_3->value())*statBuff;
    stats2.at(CUNNING) += ui->secondaryBox_3->value();
    stats2.at(CRIT) += ui->critBox_3->value();
    stats2.at(SURGE) += ui->surgeBox_3->value();
    stats2.at(POWER) += ui->powerBox_3->value() + ui->techBox_2->value();
    stats2.at(ALACRITY) += ui->alacBox_3->value();

    stats2 = calculateStats(stats2);
    if(ui->fixedTimeButton->isChecked()){
        upg2HPS = bodyguardRotation(rotationTime, stats2);
    }else{
        upg2HPS = bodyguardRotation(numCasts, stats2);
    }

    upg1HPS -= baseHPS;
    upg2HPS -= baseHPS;

    ui->HPSlabel_2->setText(QString::number(upg1HPS));
    ui->HPSlabel_3->setText(QString::number(upg2HPS));

    if(upg1HPS == upg2HPS) ui->upg3Label->setText("No difference");
    if(upg1HPS > upg2HPS) ui->upg3Label->setText("Use Item 1");
    if(upg1HPS < upg2HPS) ui->upg3Label->setText("Use Item 2");
}

void Mercenary::on_calcHPSButton_clicked()
{
updateStatBoxes();
    double HPS = calculateHPS();
    ui->castView->clear();
    ui->countView->clear();
    QString countString = "RSc casts: " + QString::number(RSc_count)+"\nHSc casts: " + QString::number(HSc_count)
            +"\nRSh casts: " + QString::number(RSh_count)+ "\nRSc casts in SCG: " + QString::number(RSc_SCG_count)
            +"\nHSc casts in SCC: " + QString::number(HSc_SCG_count) +"\nRSh casts in SCG: " + QString::number(RSh_SCG_count)
            +"\nES casts: " + QString::number(ES_count) +"\nSCG usages: " + QString::number(SCG_count)
            +"\nVH usages: " + QString::number(VH_count);
    ui->countView->insertPlainText(countString);
    ui->castView->insertPlainText(mercRotationOutput);
    ui->HPSlabel->setText(QString::number(HPS));
}

double Mercenary::bodyguardRotation(int numCasts, vector <double> &stats){
    //define Mercenary abilities

    //define Bodyguard Abilities
    //Rapid Shots (Hammer Shot)
    Ability RSh(1.5, 0, true, 1.0, 0.0, 0.0);

    //Healing Scan (Advanced Medical Probe)
    Ability HSc(1.5, 16, false, 1.94, 0.077, 0.117, 1, 0.26, 0.014, 0.014, 2);

    //Rapid Scan (Medical Probe)
    Ability RSc(2, 25, false, 2.72, 0.116, 0.156);

    //Emergency Scan (Bacta Infusion)
    Ability ES(1.5, 0, true, 2.28, 0.084, 0.144);

    mercRotationOutput = "";
    HSc_count = 0;
    RSc_count = 0;
    ES_count = 0;
    RSh_count = 0;
    VH_count = 0;
    SCG_count = 0;
    HSc_SCG_count = 0;
    RSc_SCG_count = 0;
    RSh_SCG_count = 0;

    double knight_buff = 1;
    if (ui->knightCheckBox->isChecked()) knight_buff = 1.05;
    double integratedSystemsBuff = 1+0.01*(ui->skillBox_9->value());

    int VH_use = ui->optionBox_3->value();
    int VH_sub = 6+ui->skillBox_7->value();
    int HSc_use = ui->optionBox_2->value();
    int RSc_use = ui->optionBox_1->value();
    //define set bonuses
    double reduced_HSc_CD = 0;
    double reduced_ES_CD = 0;
    double increased_SCG_Duration = 0;
    if (ui->check2pcBox->isChecked()) increased_SCG_Duration = 3;
    if (ui->check4pcBox->isChecked()){
        reduced_HSc_CD = 1.5;
        reduced_ES_CD = 3;
    }

    //get derived stats of value

    double critChance = stats.at(TECH_CRIT_CHANCE);
    double critSize = stats.at(TECH_CRIT_SIZE);
    double speed = stats.at(SPEED);
    double bonusHealing = stats.at(TECH_BONUS_HEALING);

    //initialize timers and counters
    double regenTime = 0;
    double HSc_CD_length = 9 - reduced_HSc_CD;
    double ES_CD_length = 21 - reduced_ES_CD;
    double SCG_duration = 10 + increased_SCG_Duration;
    double eTime = 0.0; //elapsed time
    double CE = 0; //time remaining on Field Triage
    int CSC = 0; //Combat Support Cell Charges
    double SCG = 0; //time remaining on Supercharge Cells
    double eHeals = 0.0; //elapsed healing
    double HSc_CD = 0; //time remaining on AP Cool down
    double ES_CD = 0; //time remaining on BI Cool down
    double VH_CD = 0; //time remaining on RC cool down
    string ability = "xx";
    Ability abil;
    double rHeat = 0;
    double tHeat = 0;
    double tTime = 0;
    double tHeal = 0;
    double Heat = 0.0; //Ammo level

    for (int n = 0; n < numCasts; n++) {

        if (CSC >= 30) {
            Heat -= 16;
            SCG = SCG_duration;
            SCG_count++;
            CSC = 0;
            HSc_CD = 0;
            Heat = min(max(Heat, 0.0), 100.0);
        }
        if (Heat > VH_use && VH_CD == 0) {
            Heat -= VH_sub;
            VH_CD = 120;
            VH_count++;
            Heat = min(max(Heat, 0.0), 100.0);
        }
        if (SCG > 0) {
            if (CE > 0 && Heat < (80 - (RSc.getAmmo()-3 * RSc.getCastTime(speed)))) {
                tHeal = RSc.getHealing(bonusHealing);
                tTime = RSc.getCastTime(speed);
                tHeat = RSc.getAmmo() - 16;
                ability = "RSc";
                RSc_count++;
                RSc_SCG_count++;

            } else {
                if (HSc_CD == 0 && Heat < (HSc_use - (HSc.getAmmo() - 3 * HSc.getCastTime(speed)+ (RSc.getAmmo() - 16 - RSc.getCastTime(speed)*3)))) {
                    tHeal = HSc.getHealing(bonusHealing);
                    tTime = HSc.getCastTime(speed);
                    tHeat = HSc.getAmmo();
                    ability = "HSc";
                    HSc_count++;
                    HSc_SCG_count++;
                } else {
                    if (ES_CD == 0) {
                        tHeal = ES.getHealing(bonusHealing);
                        tTime = ES.getCastTime(speed);
                        tHeat = ES.getAmmo();
                        ability = "ES";
                        ES_count++;
                    } else {
                        tHeal = RSh.getHealing(bonusHealing);
                        tTime = RSh.getCastTime(speed);
                        tHeat = RSh.getAmmo();
                        ability = "RSh";
                        RSh_count++;
                        RSh_SCG_count++;
                    }
                }
            }

        } else {
            if (CE > 0 && Heat < (80 - (RSc.getAmmo() - 16))) {
                tHeal = RSc.getHealing(bonusHealing);
                tTime = RSc.getCastTime(speed);
                tHeat = RSc.getAmmo() - 16;
                ability = "RSc";
                CSC += 6;
                RSc_count++;
            } else {
                if (HSc_CD == 0 && Heat < (39 - (HSc.getAmmo() - 5 * HSc.getCastTime(speed)))) {
                    tHeal = HSc.getHealing(bonusHealing);
                    tTime = HSc.getCastTime(speed);
                    tHeat = HSc.getAmmo();
                    ability = "HSc";
                    HSc_count++;
                } else {
                    if (ES_CD == 0) {
                        tHeal = ES.getHealing(bonusHealing);
                        tTime = ES.getCastTime(speed);
                        tHeat = ES.getAmmo();
                        ability = "ES";
                        ES_count++;
                    } else {
                        if (Heat < RSc_use) {
                            tHeal = RSc.getHealing(bonusHealing);
                            tTime = RSc.getCastTime(speed);
                            tHeat = RSc.getAmmo();
                            ability = "RSc";
                            CSC += 6;
                            RSc_count++;
                        } else {
                            tHeal = RSh.getHealing(bonusHealing);
                            tTime = RSh.getCastTime(speed);
                            tHeat = RSh.getAmmo();
                            ability = "RSh";
                            CSC += 3;
                            RSh_count++;
                        }
                    }
                }
            }
        }

        if(ability == "RSc")abil = RSc;
        if(ability == "HSc")abil = HSc;
        if(ability == "ES")abil = ES;
        if(ability == "RSh")abil = RSh;

        //Factor in Critical and Surge to the average heal size
        tHeal = tHeal * ((1 - critChance / 100)+(1 + critSize / 100)*(critChance / 100));

        if (SCG > 0) //Apply Supercharge Cell buff
        {
            tHeal = tHeal * 1.1;
        } else {
            tHeal = tHeal * (1 + (CSC * 0.03 / 30)); //Apply Combat Support Cell buff
        }

        //apply knight buff and integrated systems skill
        tHeal *= knight_buff*integratedSystemsBuff;
        //apply field medicine buff
        if(ability == "RSc"){
            tHeal *= 1+0.03*(ui->skillBox_8->value());
        }

        SCG = max(0.0, SCG - tTime);

        eHeals += tHeal; //increase elapsed healing
        eTime += tTime; // increase elapsed time
        rHeat = getHeatRegened(Heat, abil, eTime, regenTime);
        Heat += (tHeat - rHeat); //calculate net change in Ammo level

        //decrease time remaining on cooldowns
        VH_CD -= tTime;
        ES_CD -= tTime;
        HSc_CD -= tTime;

        //ensure levels don't exceed max or min values
        SCG = max(0.0, SCG);
        HSc_CD = max(0.0, HSc_CD);
        ES_CD = max(0.0, ES_CD);
        VH_CD = max(0.0, VH_CD);
        CE = max(0.0, CE);
        CSC = min(30, CSC);
        Heat = min(max(Heat, 0.0), 100.0);

        //set cooldowns and remove procs
        if (ability == "HSc") {
            CE = 10;
            if (SCG == 0) {
                HSc_CD = HSc_CD_length;
            } else
                HSc_CD = 0;
        }
        if (ability == "RSc") {
            CE = 0;
        }
        if (ability == "ES") {
            ES_CD = ES_CD_length;
        }
        if(ability == "RSc")
            mercRotationOutput+="RSc , ";
        if(ability == "HSc")
            mercRotationOutput+="HSc , ";
        if(ability == "ES")
            mercRotationOutput+="ES , ";
        if(ability == "RSh")
            mercRotationOutput+="RSh , ";
        mercRotationOutput+= QString::number(Heat) + " , " + QString::number(tTime) + " , " + QString::number(tHeal)/* + " , " + QString::number(SCC) + " , " + QString::number(FT)*/ + "\n";
    }
    double HPS = eHeals / eTime;

    return HPS;
}

double Mercenary::bodyguardRotation(double rotationTime, vector <double> &stats){
    //define Mercenary abilities

    //define Bodyguard Abilities
    //Rapid Shots (Hammer Shot)
    Ability RSh(1.5, 0, true, 1.0, 0.0, 0.0);

    //Healing Scan (Advanced Medical Probe)
    Ability HSc(1.5, 16, false, 1.94, 0.077, 0.117, 1, 0.26, 0.014, 0.014, 2);

    //Rapid Scan (Medical Probe)
    Ability RSc(2, 25, false, 2.72, 0.116, 0.156);

    //Emergency Scan (Bacta Infusion)
    Ability ES(1.5, 0, true, 2.28, 0.084, 0.144);

    mercRotationOutput = "";
    HSc_count = 0;
    RSc_count = 0;
    ES_count = 0;
    RSh_count = 0;
    VH_count = 0;
    SCG_count = 0;
    HSc_SCG_count = 0;
    RSc_SCG_count = 0;
    RSh_SCG_count = 0;

    double knight_buff = 1;
    if (ui->knightCheckBox->isChecked()) knight_buff = 1.05;
    double integratedSystemsBuff = 1+0.01*(ui->skillBox_9->value());

    int VH_use = ui->optionBox_3->value();
    int VH_sub = 60+8*(ui->skillBox_7->value());
    int HSc_use = ui->optionBox_2->value();
    int RSc_use = ui->optionBox_1->value();
    //define set bonuses
    double reduced_HSc_CD = 0;
    double reduced_ES_CD = 0;
    double increased_SCG_Duration = 0;
    if (ui->check2pcBox->isChecked()) increased_SCG_Duration = 3;
    if (ui->check4pcBox->isChecked()){
        reduced_HSc_CD = 1.5;
        reduced_ES_CD = 3;
    }

    //get derived stats of value

    double critChance = stats.at(TECH_CRIT_CHANCE);
    double critSize = stats.at(TECH_CRIT_SIZE);
    double speed = stats.at(SPEED);
    double bonusHealing = stats.at(TECH_BONUS_HEALING);

    //initialize timers and counters
    double regenTime = 0;
    double HSc_CD_length = 9 - reduced_HSc_CD;
    double ES_CD_length = 21 - reduced_ES_CD;
    double SCG_duration = 10 + increased_SCG_Duration;
    double eTime = 0.0; //elapsed time
    double CE = 0; //time remaining on Field Triage
    int CSC = 0; //Combat Support Cell Charges
    double SCG = 0; //time remaining on Supercharge Cells
    double eHeals = 0.0; //elapsed healing
    double HSc_CD = 0; //time remaining on AP Cool down
    double ES_CD = 0; //time remaining on BI Cool down
    double VH_CD = 0; //time remaining on RC cool down
    string ability = "xx";
    Ability abil;
    double rHeat = 0;
    double tHeat = 0;
    double tTime = 0;
    double tHeal = 0;
    double Heat = 0.0; //Ammo level

    while(eTime < rotationTime) {

        if (CSC >= 30) {
            Heat -= 16;
            SCG = SCG_duration;
            SCG_count++;
            CSC = 0;
            HSc_CD = 0;
            Heat = min(max(Heat, 0.0), 100.0);
        }
        if (Heat > VH_use && VH_CD == 0) {
            Heat -= VH_sub;
            VH_CD = 120;
            VH_count++;
            Heat = min(max(Heat, 0.0), 100.0);
        }
        if (SCG > 0) {
            if (CE > 0 && Heat < (80 - (RSc.getAmmo()-3 * RSc.getCastTime(speed)))) {
                tHeal = RSc.getHealing(bonusHealing);
                tTime = RSc.getCastTime(speed);
                tHeat = RSc.getAmmo() - 16;
                ability = "RSc";
                RSc_count++;
                RSc_SCG_count++;

            } else {
                if (HSc_CD == 0 && Heat < (HSc_use - (HSc.getAmmo() - 3 * HSc.getCastTime(speed)+ (RSc.getAmmo() - 16 - RSc.getCastTime(speed)*3)))) {
                    tHeal = HSc.getHealing(bonusHealing);
                    tTime = HSc.getCastTime(speed);
                    tHeat = HSc.getAmmo();
                    ability = "HSc";
                    HSc_count++;
                    HSc_SCG_count++;
                } else {
                    if (ES_CD == 0) {
                        tHeal = ES.getHealing(bonusHealing);
                        tTime = ES.getCastTime(speed);
                        tHeat = ES.getAmmo();
                        ability = "ES";
                        ES_count++;
                    } else {
                        tHeal = RSh.getHealing(bonusHealing);
                        tTime = RSh.getCastTime(speed);
                        tHeat = RSh.getAmmo();
                        ability = "RSh";
                        RSh_count++;
                        RSh_SCG_count++;
                    }
                }
            }

        } else {
            if (CE > 0 && Heat < (80 - (RSc.getAmmo() - 16))) {
                tHeal = RSc.getHealing(bonusHealing);
                tTime = RSc.getCastTime(speed);
                tHeat = RSc.getAmmo() - 16;
                ability = "RSc";
                CSC += 6;
                RSc_count++;
            } else {
                if (HSc_CD == 0 && Heat < (39 - (HSc.getAmmo() - 5 * HSc.getCastTime(speed)))) {
                    tHeal = HSc.getHealing(bonusHealing);
                    tTime = HSc.getCastTime(speed);
                    tHeat = HSc.getAmmo();
                    ability = "HSc";
                    HSc_count++;
                } else {
                    if (ES_CD == 0) {
                        tHeal = ES.getHealing(bonusHealing);
                        tTime = ES.getCastTime(speed);
                        tHeat = ES.getAmmo();
                        ability = "ES";
                        ES_count++;
                    } else {
                        if (Heat < RSc_use) {
                            tHeal = RSc.getHealing(bonusHealing);
                            tTime = RSc.getCastTime(speed);
                            tHeat = RSc.getAmmo();
                            ability = "RSc";
                            CSC += 6;
                            RSc_count++;
                        } else {
                            tHeal = RSh.getHealing(bonusHealing);
                            tTime = RSh.getCastTime(speed);
                            tHeat = RSh.getAmmo();
                            ability = "RSh";
                            CSC += 3;
                            RSh_count++;
                        }
                    }
                }
            }
        }

        if(ability == "RSc")abil = RSc;
        if(ability == "HSc")abil = HSc;
        if(ability == "ES")abil = ES;
        if(ability == "RSh")abil = RSh;

        //Factor in Critical and Surge to the average heal size
        tHeal = tHeal * ((1 - critChance / 100)+(1 + critSize / 100)*(critChance / 100));

        if (SCG > 0) //Apply Supercharge Cell buff
        {
            tHeal = tHeal * 1.1;
        } else {
            tHeal = tHeal * (1 + (CSC * 0.03 / 30)); //Apply Combat Support Cell buff
        }

        //apply knight buff
        tHeal *= knight_buff*integratedSystemsBuff;
        //apply field medicine buff
        if(ability == "RSc"){
            tHeal *= 1+0.03*(ui->skillBox_8->value());
        }

        SCG = max(0.0, SCG - tTime);

        eHeals += tHeal; //increase elapsed healing
        eTime += tTime; // increase elapsed time
        rHeat = getHeatRegened(Heat, abil, eTime, regenTime);
        Heat += (tHeat - rHeat); //calculate net change in Ammo level

        //decrease time remaining on cooldowns
        VH_CD -= tTime;
        ES_CD -= tTime;
        HSc_CD -= tTime;

        //ensure levels don't exceed max or min values
        SCG = max(0.0, SCG);
        HSc_CD = max(0.0, HSc_CD);
        ES_CD = max(0.0, ES_CD);
        VH_CD = max(0.0, VH_CD);
        CE = max(0.0, CE);
        CSC = min(30, CSC);
        Heat = min(max(Heat, 0.0), 100.0);

        //set cooldowns and remove procs
        if (ability == "HSc") {
            CE = 10;
            if (SCG == 0) {
                HSc_CD = HSc_CD_length;
            } else
                HSc_CD = 0;
        }
        if (ability == "RSc") {
            CE = 0;
        }
        if (ability == "ES") {
            ES_CD = ES_CD_length;
        }
        //        mercRotationOutput+= QString::number(Heat) +" , " +QString::number(rHeat) + " , " + QString::number(tHeat) + "\n";
        if(ability == "RSc")
            mercRotationOutput+="RSc , ";
        if(ability == "HSc")
            mercRotationOutput+="HSc , ";
        if(ability == "ES")
            mercRotationOutput+="ES , ";
        if(ability == "RSh")
            mercRotationOutput+="RSh , ";
        mercRotationOutput+= QString::number(Heat) + " , " + QString::number(tTime) + " , " + QString::number(tHeal)/* + " , " + QString::number(SCC) + " , " + QString::number(FT)*/ + "\n";
    }
    //mercRotationOutput+= QString::number(RSc.getAmmo()) + " \n " + QString::number(HSc.getAmmo()) + "  \n " + QString::number(RSh.getAmmo()) + "  \n " + QString::number(ES.getAmmo()) + " \n ";
    double HPS = eHeals / eTime;

    return HPS;
}

void Mercenary::on_primaryBox_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_powerBox_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_critBox_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_alacBox_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_levelBox_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_secondaryBox_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_techBox_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_surgeBox_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_accBox_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_MHmaxBox_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_MHminBox_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_enemyLevelBox_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_enemyArmorBox_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_calcWeightButton_clicked()
{
    updateStatBoxes();
    getWeights();
}

void Mercenary::on_upgradeButton_clicked()
{
    compareUpgrades();
}

void Mercenary::on_check4pcBox_toggled(bool checked)
{
    if(checked==true) ui->check2pcBox->setChecked(true);
}

void Mercenary::on_check2pcBox_toggled(bool checked)
{
    if(checked==false) ui->check4pcBox->setChecked(false);
}


void Mercenary::on_computeAlacrityButton_clicked()
{
    computeAlacrity();
}

void Mercenary::on_skillBox_1_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_skillBox_2_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_skillBox_3_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_skillBox_4_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_skillBox_5_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_skillBox_6_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_skillBox_7_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_skillBox_8_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_skillBox_9_editingFinished()
{
    updateStatBoxes();
}

void Mercenary::on_note1_clicked()
{
    ui->countView->clear();
    QString note1 = "*Note that Crit Size may differ from the value in game because Warden only affects heals, so does not show up on the in-game tooltip.";
    ui->countView->insertPlainText(note1);
}

void Mercenary::on_note2_clicked()
{
    ui->countView->clear();
    QString note2 = "**Note 2: Alacrity has complex interactions with SCG windows, resource mechanics, Effective Cost of abilities, etc.\n\n";
    note2 += "Current recommendation is for 5-7.5% Alacrity, not to exceed 8.5% for Commandos or 12.5% for Mercs. The difference is due to imbalance in their resource regen windows.  While Alacrity is not good for Mercs, it is especially bad for Commandos.\n\n";
    note2 += "Note that these limits become less of a concern as you overgear your encounters and you increase your downtime. That turns the encounter from a sustained healing to a burst healing mode with recovery times, and the increased Effective Cost is less problematic. Use your best judgment.\n\n";
    note2 += "For more information, see the following two threads on the SWTOR.com forums\n\n http://www.swtor.com/community/showthread.php?p=2900243#post2900243\n\nhttp://www.swtor.com/community/showthread.php?t=306667";
    ui->countView->insertPlainText(note2);
}

void Mercenary::on_saveStats_clicked()
{
    setStats();
    string filename = "Mercenary_stats.txt";
    ofstream fout (filename.c_str());
    fout << mercStats.at(AIM) << "\n" << mercStats.at(CUNNING) << "\n" << mercStats.at(CRIT)<< "\n" <<
            mercStats.at(SURGE) << "\n" << ui->powerBox->value() << "\n" << ui->techBox->value() << "\n" << mercStats.at(ALACRITY) << "\n" <<
    mercStats.at(LEVEL) << "\n" << mercStats.at(ACCURACY) << "\n" << mercStats.at(MH_MAX) << "\n" <<
    mercStats.at(MH_MIN) << "\n" << mercStats.at(ENEMY_LEVEL) << "\n" << mercStats.at(ENEMY_ARMOR) << endl;

    fout << ui->skillBox_1->value() << "\n" << ui->skillBox_2->value() << "\n" << ui->skillBox_3->value() << "\n" <<
            ui->skillBox_4->value() << "\n" << ui->skillBox_5->value() << "\n" << ui->skillBox_6->value() << "\n" <<
            ui->skillBox_7->value() << "\n" << ui->skillBox_8->value() << "\n" << ui->skillBox_9->value() << "\n" << endl;

    fout << mercStats.at(OH_MAX) << "\n" << mercStats.at(OH_MIN) << endl;
    fout.close();
}

void Mercenary::on_loadStats_clicked()
{
    Load_Stats();
}

void Mercenary::Load_Stats(){
    string filename = "Mercenary_stats.txt";
    ifstream fin (filename.c_str());
    vector <string> inputStats(27,"0");
    int i=0;
    while(fin.good()){
        getline(fin,inputStats.at(i));
                i++;
    }
    ui->primaryBox->setValue(atoi(inputStats.at(0).c_str()));
    ui->secondaryBox->setValue(atoi(inputStats.at(1).c_str()));
    ui->critBox->setValue(atoi(inputStats.at(2).c_str()));
    ui->surgeBox->setValue(atoi(inputStats.at(3).c_str()));
    ui->powerBox->setValue(atoi(inputStats.at(4).c_str()));
    ui->techBox->setValue(atoi(inputStats.at(5).c_str()));
    ui->alacBox->setValue(atoi(inputStats.at(6).c_str()));
    ui->levelBox->setValue(atoi(inputStats.at(7).c_str()));
    ui->accBox->setValue(atoi(inputStats.at(8).c_str()));
    ui->MHmaxBox->setValue(atoi(inputStats.at(9).c_str()));
    ui->MHminBox->setValue(atoi(inputStats.at(10).c_str()));
    ui->enemyLevelBox->setValue(atoi(inputStats.at(11).c_str()));
    ui->enemyArmorBox->setValue(atoi(inputStats.at(12).c_str()));

//set skill boxes
    ui->skillBox_1->setValue(atoi(inputStats.at(13).c_str()));
    ui->skillBox_2->setValue(atoi(inputStats.at(14).c_str()));
    ui->skillBox_3->setValue(atoi(inputStats.at(15).c_str()));
    ui->skillBox_4->setValue(atoi(inputStats.at(16).c_str()));
    ui->skillBox_5->setValue(atoi(inputStats.at(17).c_str()));
    ui->skillBox_6->setValue(atoi(inputStats.at(18).c_str()));
    ui->skillBox_7->setValue(atoi(inputStats.at(19).c_str()));
    ui->skillBox_8->setValue(atoi(inputStats.at(20).c_str()));
    ui->skillBox_9->setValue(atoi(inputStats.at(21).c_str()));

    //Off-hand stats
    ui->OHmaxBox->setValue(atoi(inputStats.at(22).c_str()));
    ui->OHminBox->setValue(atoi(inputStats.at(23).c_str()));

    fin.close();
}

void Mercenary::on_tree1_clicked()
{
    updateStatBoxes();
}

void Mercenary::on_tree2_clicked()
{
    updateStatBoxes();
}

void Mercenary::on_tree3_clicked()
{
    updateStatBoxes();
}
